package com.opNaga.bpmp.swimlane;

import com.opNaga.bpmp.BPMPElement;
import com.opNaga.bpmp.requirement.*;
import com.opNaga.bpmp.user.User;

import java.util.*;

/**
 * User: Junyi BEI
 * Date: 19/01/2012
 * Time: 12:19
 */
public abstract class Swimlane extends BPMPElement implements Container, HasPositionRequirement, HasAbilityRequirement {
  protected BPMPElement root;
  protected Set<BPMPElement> elements = new HashSet<BPMPElement>();
  protected Set<BPMPElement> positionRequirements = new HashSet<BPMPElement>();
  protected Set<BPMPElement> abilityRequirements = new HashSet<BPMPElement>();

  public Swimlane() {
    super();
  }

  public Swimlane(String t) {
    super(t);
  }

  public boolean destroy() {
    Iterator<BPMPElement> beIt = iterateElements();
    while(beIt.hasNext()) {
      BPMPElement be = beIt.next();
      if(!be.destroy()) {
        return standardMessage(820, be);
      }
    }
    return super.destroy();
  }

  public boolean contains(BPMPElement be) {
    return elements.contains(be);
  }

  public boolean isEmpty() {
    return elements.isEmpty();
  }

  public int size() {
    return elements.size();
  }

  public Iterator<BPMPElement> iterateElements() {
    return new ArrayList<BPMPElement>(elements).iterator();
  }

  public boolean addPositionRequirement(BPMPElement bc) {
    if(!PositionRequirement.class.isAssignableFrom(bc.getClass())) {
      return standardMessage(760, bc);
    }
    if(!positionRequirements.add(bc)) {
      return standardMessage(761, bc);
    }
    return standardMessage(768, bc);
  }

  public boolean removePositionRequirement(BPMPElement bc) {
    if(!PositionRequirement.class.isAssignableFrom(bc.getClass())) {
      return standardMessage(762, bc);
    }
    if(!positionRequirements.remove(bc)) {
      return standardMessage(763, bc);
    }
    return standardMessage(769, bc);
  }

  public Iterator<BPMPElement> iteratePositionRequirements() {
    return new ArrayList<BPMPElement>(positionRequirements).iterator();
  }

  public boolean addAbilityRequirement(BPMPElement bc) {
    if(!AbilityRequirement.class.isAssignableFrom(bc.getClass())) {
      return standardMessage(750, bc);
    }
    if(!abilityRequirements.add(bc)) {
      return standardMessage(751, bc);
    }
    return standardMessage(758, bc);
  }

  public boolean removeAbilityRequirement(BPMPElement bc) {
    if(!AbilityRequirement.class.isAssignableFrom(bc.getClass())) {
      return standardMessage(752, bc);
    }
    if(!abilityRequirements.remove(bc)) {
      return standardMessage(753, bc);
    }
    return standardMessage(759, bc);
  }

  public Iterator<BPMPElement> iterateAbilityRequirements() {
    return new ArrayList<BPMPElement>(abilityRequirements).iterator();
  }

  public boolean filterAgainstAbilityRequirements(Collection<BPMPElement> bes) {
    int before = bes.size();
    for(BPMPElement be : new ArrayList<BPMPElement>(bes)) {
      Iterator<BPMPElement> arIt = iterateAbilityRequirements();
      while(arIt.hasNext()) {
        BPMPElement ar = arIt.next();
        Iterator<BPMPElement> aIt = ((User) be).iterateAbilities();
        boolean capable = false;
        while(aIt.hasNext()) {
          BPMPElement a = aIt.next();
          Collection<BPMPElement> ac = ((AbilityRequirement) a).getCandidates();
          if(ac.contains(ar)) {
            capable = true;
            standardMessage(993, be, ar);
            break;
          }
        }
        if(!capable) {
          bes.remove(be);
          standardMessage(994, be, ar);
          break;
        }
      }
    }
    int after = bes.size();
    return standardMessage(990, after, before);
  }

  public boolean filterAgainstPositionRequirements(Collection<BPMPElement> bes) {
    int before = bes.size();
    for(BPMPElement be : new ArrayList<BPMPElement>(bes)) {
      Iterator<BPMPElement> prIt = iteratePositionRequirements();
      while(prIt.hasNext()) {
        BPMPElement pr = prIt.next();
        Iterator<BPMPElement> pIt = ((User) be).iteratePositions();
        boolean abovePosition = false;
        while(pIt.hasNext()) {
          BPMPElement p = pIt.next();
          Collection<BPMPElement> pc = ((PositionRequirement) p).getCandidates();
          if(pc.contains(pr)) {
            abovePosition = true;
            standardMessage(993, be, pr);
            break;
          }
        }
        if(!abovePosition) {
          bes.remove(be);
          standardMessage(994, be, pr);
          break;
        }
      }
    }
    int after = bes.size();
    return standardMessage(992, after, before);
  }

  public boolean filterAgainstAllRequirements(Collection<BPMPElement> bes) {
    BPMPElement parent = getParent();
    if(!isRoot()) {
      if(parent != null) {
        if(HasRequirement.class.isAssignableFrom(parent.getClass())) {
          if(!((HasRequirement) parent).filterAgainstAllRequirements(bes)) {
            return standardMessage(985, parent);
          }
        } else {
          return standardMessage(984, parent);
        }
      } else {
        return standardMessage(983);
      }
    }
    return filterAgainstAbilityRequirements(bes) && filterAgainstPositionRequirements(bes);
  }

  public boolean setRoot(BPMPElement root) {
    Iterator<BPMPElement> beIt = this.iterateElements();
    while(beIt.hasNext()) {
      BPMPElement be = beIt.next();
      if(!HasRoot.class.isAssignableFrom(be.getClass())) {
        return standardMessage(700, root, be);
      }
      if(!((HasRoot) be).setRoot(root)) {
        return standardMessage(701, root, be);
      }
    }
    if(Pool.class.isAssignableFrom(getClass())) {
      Iterator<BPMPElement> lIt = ((Pool) this).iterateLanes();
      while(lIt.hasNext()) {
        BPMPElement be = beIt.next();
        if(!HasRoot.class.isAssignableFrom(be.getClass())) {
          return standardMessage(702, root, be);
        }
        if(!((HasRoot) be).setRoot(root)) {
          return standardMessage(703, root, be);
        }
      }
    }
    this.root = root;
    return standardMessage(740, root);
  }
}
